package utils.output;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.Deflater;

import pluginFactory.PluginsCharger;
import utils.Messages;

/**
 * Class used to create the jar file output.
 * 
 * @author Andy Cullache
 */
public class OutputJarFile {
	
	private static JarOutputStream outJar;

	/**
	 * Creates the jar file from a name, directory and directory recommendation algorithm.
	 * 
	 * @param outputName      filename to be generated.
	 * @param directory      directory file to generate.
	 * @param pluginDirectory      Plugin directory of recommendation algorithm to include in the jar file.
	 * @return      True if jar file is generated, false otherwise.
	 * @throws IOException
	 */
    public boolean generateJarOutput(String outputName, String directory, String pluginDirectory) throws IOException{
    	String temp = "Temp";
    	boolean createJar = false;
    	if(pluginDirectory != null){
	    	extractJar(pluginDirectory,new File(temp));
    	}
    	extractJar("lib/resourcesToOutput.jar",new File(temp));
    	for(File file : PluginsCharger.Finder("plugins//similarity")){
    		extractJar(file.getPath(),new File(temp));
    	}
    	for(File file : PluginsCharger.Finder("plugins//composition")){
    		extractJar(file.getPath(),new File(temp));
    	}
    	createJar = writeJar(outputName+".jar", directory, temp);
    	outJar.close();
    	return  createJar;
    }  
    
    /**
     * Creates the jar file from a directory that represents the content you will have the component 
     * output is used in the method generateJarOutput ().
     * 
     * @param outputName      filename to be generated.
     * @param outputDirectory      directory file to generate.
     * @param inputDirectory      directory containing the files to include in the jar file (temporal directory).
     * @return      True if jar file is generated, false otherwise.
     */
    private static boolean writeJar(String outputName, String outputDirectory, String inputDirectory){
        File jarFile = new File(outputDirectory, outputName);
        File dir = new File(inputDirectory);  
        File outDirectory = new File(outputDirectory);   
        try {
	        if ( !dir.exists() ){  
//	            dir.mkdirs();  
	            return false;
	        }
	        if ( !outDirectory.exists() ){  
	        	outDirectory.mkdirs();
	        }
	        outJar = new JarOutputStream(new FileOutputStream(jarFile));  
	        outJar.setLevel(Deflater.DEFAULT_COMPRESSION);  
			add(dir);
	        deleteDirectory(dir);  
		} catch (IOException e) {
			System.out.println(Messages.errorCreateJARCLI);
			System.out.println(e.getMessage());
		} 
        return true;
    }
    
    /**
     * Add a file or entire directory to the folder containing the files that will be included in the jar component.
     * 
     * @param source      File or directory to add in jar component. 
     * @throws IOException
     */
    private static void add(File source) throws IOException{
    	  BufferedInputStream in = null;
    	  try
    	  {
    	    if (source.isDirectory())
    	    {
    	      String name = source.getPath().replace("\\", "/");
    	      if (!name.isEmpty() && !name.contains("Temp"))
    	      {
    	        if (!name.endsWith("/"))
    	          name += "/";
    	        JarEntry entry = new JarEntry(name);
    	        entry.setTime(source.lastModified());
    	        outJar.putNextEntry(entry);
    	        outJar.closeEntry();
    	      }
    	      for (File nestedFile: source.listFiles())
    	        add(nestedFile);
    	      return;
    	    }
    	    String aux = source.getPath().replace("\\", "/");
    	    JarEntry entry;
    	    if(aux.startsWith("Temp")){
    	    	entry = new JarEntry(aux.substring(5));
    	    }
    	    else{
    	    	entry = new JarEntry(aux);
    	    }
    	    entry.setTime(source.lastModified());
    	    outJar.putNextEntry(entry);
    	    in = new BufferedInputStream(new FileInputStream(source));

    	    byte[] buffer = new byte[1024];
    	    while (true)
    	    {
    	      int count = in.read(buffer);
    	      if (count == -1)
    	        break;
    	      outJar.write(buffer, 0, count);
    	    }
    	    outJar.closeEntry();
    	  }
    	  finally
    	  {
    	    if (in != null)
    	      in.close();
    	  }
    }
    
    /**
     * Delete a file or directory on your hard disk.
     * 
     * @param directory      File or directory to delete.
     */
    private static void deleteDirectory (File directory){
    	for(File file : directory.listFiles()){
    		if (file.isDirectory()) {
    			deleteDirectory(file);
    		}
    		file.delete();
    	}
    	directory.delete();
    }
    
    /**
     * Extract a jar file, given as a parameter in the parameter directory.
     * 
     * @param pluginDirectory      Jar file to extract.
     * @param tempDirectory      Directory where the files are extracted from the jar.
     * @throws IOException
     */
    private static void extractJar(String pluginDirectory, File tempDirectory)throws IOException{
    	
    	JarInputStream jarInput = new JarInputStream(new FileInputStream(pluginDirectory));
    	JarEntry jarEntry=null;
    	
    	while((jarEntry=jarInput.getNextJarEntry())!=null){
    		File file = new File(tempDirectory,jarEntry.getName());
    		if (jarEntry.isDirectory()){
    			if (!file.exists())
    				file.mkdirs();
    		}
    	    else{
    	    	File dir = new File(file.getParent());
    	    	if (!dir.exists()){
    	    		dir.mkdirs();
    	    	}
    	    	byte[] bytes = new byte[1024];
    	    	InputStream inputStream   = new BufferedInputStream(jarInput);
    	    	FileOutputStream fileOutputStream = new FileOutputStream(file);
    	    	
    	    	int read = -1;
    	        while ((read = inputStream.read(bytes)) != -1) {
    	        	fileOutputStream.write(bytes, 0, read);
    	        }
    	        fileOutputStream.close();
    	    }
    	}
    }
    
}  
      